#include <Trie.hpp>
#include <iostream>
#include <stack>

Trie::Trie() : qtyChildren(0) {
	children = std::vector<Trie *>(52, (Trie*)0);
}

Trie::~Trie() {
	for (int i = 0; i < 52; i++)
		if (children[i])
			delete children[i];
}

int Trie::convertToIndice(char letter) {
	if (letter >= 'a' && letter <= 'z')
		return letter - 'a';
	else if (letter >= 'A' && letter <= 'Z')
		return (letter - 'A') + 26;
	else return -1;
}

void Trie::insert(std::string word) {
	insert(word, word);
}

void Trie::insert(std::string word, std::string value) {
	int sz = word.length();
	Trie * curnode = this;

	for (int i = 0; i < sz; i++) {
		int pos = convertToIndice(word[i]);
		if (!curnode->children[pos]) {
			curnode->children[pos] = new Trie();
			curnode->qtyChildren++;
		}
		curnode = curnode->children[pos];
	}
	if (!(curnode->values.size() == 1 && curnode->values[0] == value))
		curnode->values.push_back(value);
}

std::vector<std::string> Trie::retrieveUntil(std::string& word) {
	int sz = word.length();
	Trie * curnode = this;

	for (int i = 0; i < sz; i++) {
		int pos = convertToIndice(word[i]);
		if (!curnode->children[pos])
			return std::vector<std::string>();
		curnode = curnode->children[pos];
		
		if (curnode->values.size()) {
			word = word.substr(0, i+1);
			std::vector<std::string> ret = curnode->values;
			curnode->values.clear();
			return ret;
		}
	}
	return curnode->values;
}

std::vector<std::string> Trie::retrieve(std::string word) {
	int sz = word.length();
	Trie * curnode = this;

	for (int i = 0; i < sz; i++) {
		int pos = convertToIndice(word[i]);
		if (!curnode->children[pos])
			return std::vector<std::string>();
		curnode = curnode->children[pos];
	}
	return curnode->values;
}

char Trie::wordBuffer[1024];
void Trie::serialize(std::ostream& stream, int head) {
	std::list<int>::iterator it, itEnd;
	if(!head) wordBuffer[0] = 0;
	wordBuffer[head + 1] = 0;
	for(int it = 0; it != 52; it++) {
		if (!children[it]) continue;
		stream << convertToChar(it);
		wordBuffer[head] = convertToChar(it);
		children[it]->serialize(stream, head + 1);
	}
	wordBuffer[head] = 0;
	if (values.empty())
		stream << '.';
	else if (values.size() == 1 && values[0] == wordBuffer) 
		stream << '!';
	else {
		stream << '(';
		for (int i = 0; i < values.size(); i++) {
			stream << values[i] << ' ';
		}
		stream << ") ";
	}
}

int Trie::deserialize(std::istream& stream) {
	char curr;
	std::string str, key;
	char buffer[1024];
	int head = 0;
	buffer[0] = 0;
	int counter = 0;
	for (;;) {
		stream >> curr;
		if (curr == '.') {
			if(!head) break;
			head--;
			buffer[head] = 0;
		} else if (curr == '!') {
			str = buffer;
			insert(str);
			counter++;
			if(!head) break;
			head--;
			buffer[head] = 0;
		} else if (curr == '(') {
			key = buffer;
			for(stream >> str; str != ")"; stream >> str) {
				counter++;
				insert(key, str);
			}
			if(!head) break;
			head--;
			buffer[head] = 0;
		} else {
			buffer[head] = curr;
			head++;
			buffer[head] = 0;
		}
	}
	return counter;
}


char Trie::convertToChar(int pos) {
	if (pos < 26) return 'a' + (char)pos;
	else return 'A' + ((char)pos - 26);
}

Trie * Trie::detach(std::string word) {
	int sz = word.length();
	Trie * curnode = this;

	std::stack<std::pair<int, Trie*> > stack;

	int pos;
	for (int i = 0; i < sz; i++) {
		pos = convertToIndice(word[i]);
		if (!curnode->children[pos])
			return 0;
		stack.push(std::pair<int, Trie*>(pos, curnode));
		curnode = curnode->children[pos];
	}

	while (!stack.empty()) {
		std::pair<int, Trie*> top = stack.top();
		stack.pop();
		top.second->children[top.first] = 0;
		if (--top.second->qtyChildren)
			break;
		if (!stack.empty())
			delete top.second;
	}

	return curnode;
}
